package org.softmed.neodatis.util.id;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.softmed.neodatis.util.reflection.ClassGeneratedFieldInfo;
import org.softmed.neodatis.util.reflection.ClassIDFieldRegistration;
import org.softmed.reflection.utils.FieldInfo;
import org.softmed.reflection.utils.ReflectionUtil;

public class ClassFieldIDProcessor {

	ReflectionUtil util = new ReflectionUtil();

	public void process(ClassIDFieldRegistration registration,
			ClassFieldIDGenerators generators, Object object) throws Throwable {
		processStep(registration, generators, object);
	}

	protected void processStep(ClassIDFieldRegistration registration,
			ClassFieldIDGenerators generators, Object object) throws Throwable {

		processCycle(registration, generators, object.getClass(), object);
		List<FieldInfo> fields = util.getNonNativeFieldInfos(object.getClass());
		for (FieldInfo fieldInfo : fields) {
			if (List.class.isAssignableFrom(fieldInfo.getType())) {
				Field field = util.getField(object.getClass(), fieldInfo
						.getName());
				Class listType = util.isSpecificList(field);
				if (listType == null)
					continue;

				List list = (List) util.getFieldValue(object, fieldInfo);
				if (list == null || list.size() == 0)
					continue;

				for (Object child : list) {
					processStep(registration, generators, child);
				}

			}

			Object child = fieldInfo.getGetter().invoke(object, null);
			if (child != null)
				processStep(registration, generators, child);
		}
	}

	protected void processCycle(ClassIDFieldRegistration registration,
			ClassFieldIDGenerators generators, Class type, Object object)
			throws Throwable {

		if (type == null)
			return;

		processClass(registration, generators, type, object);
		if (registration.areAllInheritedGeneratedFieldsDisabled(type))
			return;

		processCycle(registration, generators, type.getSuperclass(), object);

	}

	protected void processClass(ClassIDFieldRegistration registration,
			ClassFieldIDGenerators generators, Class type, Object object)
			throws Throwable {

		ClassGeneratedFieldInfo classFieldInfo = registration
				.getClassFieldInfo(type);

		ClassGeneratedFieldInfo disabledFields = registration
				.getDisabledInheritedGeneratedFields(object.getClass());

		if (classFieldInfo == null)
			return;

		Map<String, FieldInfo> fields = classFieldInfo.getFields();
		Set<Entry<String, FieldInfo>> entries = fields.entrySet();
		Object oldId = null;
		Object id = null;
		String fieldName = null;
		FieldInfo info = null;
		Method getter = null;
		Method setter = null;
		for (Entry<String, FieldInfo> entry : entries) {
			fieldName = entry.getKey();
			info = entry.getValue();

			if (disabledFields != null) {
				FieldInfo disabledInheritedField = disabledFields
						.getFieldInfo(fieldName);
				if (disabledInheritedField != null)
					continue;
			}

			setter = info.getSetter();
			getter = info.getGetter();

			oldId = null;
			oldId = getter.invoke(object, null);

			// if there is already an assigned value, then don't generate a new
			// key
			if (oldId != null)
				continue;

			id = generators.getNextID(type, fieldName, object);

			Object[] args = new Object[1];
			args[0] = id;
			setter.invoke(object, args);
		}

	}
}
